﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Web.Services.Protocols;
using Microsoft.SharePoint;
using MSPE.Public.FOSS.AutoStatusPlus.Common;

namespace MSPE.Public.FOSS.AutoStatusPlus.TimerJob
{
    internal class UpdateStatus
    {
        #region Public Properties
        public string HostName { get; set; }
        #endregion // Public Properties

        #region Private / Protected
        private AppLog _appLog;
        private EventEntry _eventLog;
        private ResourceSvc.Resource _resource;
        private Settings _loadedSettings;
        private StatusingDerived _statusing;
        private const string _eventSourceName = "AutoStatusPlus StatusingTimerJob";
        private TimesheetSvc.TimeSheet _timesheet;
        private WebServiceManager _webRefMgr;
        #endregion // Private / Protected

        #region Constructors
        /// <summary>
        /// Constructs a new instance of the <see cref="UpdateStatus" /> class.
        /// </summary>
        public UpdateStatus() : this(String.Empty)
        {
        }

        /// <summary>
        /// Constructs a new instance of the <see cref="UpdateStatus" /> class.
        /// </summary>
        /// <param name="hostName">SSP server host name</param>
        public UpdateStatus(string hostName)
        {
            HostName = hostName;
        } // UpdateStatus()
        #endregion // Constructors

        #region Methods
        public bool ProcessTimesheet(Guid siteGuid, Guid tsGuid, string lcid, string userName, bool submitStatus, string sspName)
        {
            #region Local Variables
            bool _result = false;
            SPSite _spSite = null;
            Stopwatch _timeToProcessAll = new Stopwatch();
            Stopwatch _timeToProcessStatus = new Stopwatch();
            string _exceptionMsg = null;
            string _tsInfo = String.Empty;
            #endregion // Local variables

            try
            {
                _webRefMgr = new WebServiceManager();
                _loadedSettings = Utility.LoadSettings(siteGuid);
                _eventLog = new EventEntry(_eventSourceName);

                _timeToProcessAll.Start();
                _spSite = new SPSite(siteGuid);

                string _logFilePrefix = _loadedSettings.LogFilePrefix;

                if (_logFilePrefix == "Undefined")
                    _logFilePrefix = String.Empty;

                if (_loadedSettings.LogUpdates && _appLog == null)
                    _appLog = new AppLog(_logFilePrefix, _loadedSettings.LogFilePath, _loadedSettings.NewLogFileEveryHour);
                
                #region STEP 1 - Initialize Web Service References
                _webRefMgr.PwaUrl = _spSite.Url;
                _webRefMgr.SspUrl = Utility.BuildSspUrl(_spSite, HostName, sspName);

                _resource = _webRefMgr.GetWebRef(WebServiceTypeEnum.Resource) as ResourceSvc.Resource;
                _statusing = _webRefMgr.GetWebRef(WebServiceTypeEnum.StatusingDerived) as StatusingDerived;
                _timesheet = _webRefMgr.GetWebRef(WebServiceTypeEnum.TimeSheet) as TimesheetSvc.TimeSheet;

                // Ensure everything casted correctly
                if (_resource == null || _statusing == null || _timesheet == null)
                    throw new Exception("Unable to initialize web service references!");

                if (_loadedSettings.LogUpdates)
                    _appLog.WriteLine(string.Format("STEP 1 - Initialize Web Service References"));
                #endregion // STEP 1

                #region STEP 2 - Retrieve Timesheet DataSet
                TimesheetSvc.TimesheetDataSet _timesheetDs = _timesheet.ReadTimesheet(tsGuid);
                TimesheetHeader _tsHeader = new TimesheetHeader(_timesheetDs);
                Guid _tspUid = _tsHeader.PeriodUid;
                Guid _tresUid = _tsHeader.ResUid;

                if (_loadedSettings.LogUpdates)
                {
                    _appLog.WriteLine(string.Format("STEP 2 - Start event override for timesheet UID: {0}", tsGuid));
                    _tsInfo = string.Format("Timesheet: {0}; Resource: {1}; Creator: {2}, Period: {3}",
                            _tsHeader.Name, _tsHeader.ResName, _tsHeader.CreatorResName, _tspUid.ToString());
                } // End if
                #endregion // STEP 2

                #region STEP 3 - Perform statusing impersonation taking surrogate into account
                // Need to retrieve resource information to do proper impersonation
                ResourceSvc.ResourceDataSet _resourceDs = _resource.ReadResource(_tresUid);
                Resource _resourceData = new Resource(_resourceDs);

                // Check if surrogate timesheet
                // If owner is different than creator or where timesheet was modified by TS manager
                if ((_tsHeader.CreatorResUid != _tsHeader.ResUid) || (!_resourceData.WindowsAccount.Equals(userName)))
                    _statusing.SetImpersonationContext(_resourceData.IsWindowsUser, _resourceData.WindowsAccount, _tresUid, Guid.Empty, siteGuid, lcid);

                else
                {
                    // Faster check that calling the resource web service
                    bool _isWindowsUser = userName.StartsWith("AspNetSqlMembershipProvider") ? false : true;
                    _statusing.SetImpersonationContext(_isWindowsUser, userName, _tresUid, Guid.Empty, siteGuid, lcid);
                } // End else

                if (_loadedSettings.LogUpdates)
                    _appLog.WriteLine(string.Format("STEP 3 - Statusing Impersonation for RES_UID: {0}", _tresUid.ToString()));
                #endregion // STEP 3

                #region STEP 4 - Import Timesheet, will get an error if TS period is not within the Task's Start & Finish
                if (_tspUid != Guid.Empty)
                    _statusing.ImportTimesheet(_tspUid);

                if (_loadedSettings.LogUpdates)
                {
                    _appLog.WriteLine(
                        string.Format(
                            "STEP 4 - Imported Timesheet: {0}; Resource: {1}; Creator: {2}, TotalActValue: {3}, Period: {4}",
                            _tsHeader.Name, _tsHeader.ResName, _tsHeader.CreatorResName, _tsHeader.TotalActValue.ToString("N2"), 
                            _tspUid.ToString()));
                } // End if
                #endregion // STEP 4

                #region STEP 5 - Submit status update to project manager
                if (_loadedSettings.SubmitUpdates)
                {
                    _timeToProcessStatus.Start();
                    List<Guid> _assignmentsUid = Status.SubmitStatus(_timesheetDs, _appLog, _loadedSettings.LogUpdates, _loadedSettings.ProcessBlankLines);

                    if (_assignmentsUid.Count > 0)
                        _statusing.SubmitStatus(_assignmentsUid.ToArray(), "Submitted to statusing");

                    if (_loadedSettings.LogUpdates)
                        _appLog.WriteLine(string.Format("STEP 5 - SubmitStatus for: {0} assignments", _assignmentsUid.Count.ToString()));

                    _timeToProcessStatus.Stop();
                } // End if

                _result = true;
                #endregion // STEP 5
            } // End try
            
            #region STEP 6 - Handle Exceptions and logging
            catch (SoapException ex)
            {
                _exceptionMsg = Utility.ExpandPsiException(ex).Message;
                _eventLog.WriteEvent("FAILED due to a SoapException: " + _exceptionMsg);
                if (_appLog != null && _exceptionMsg != null) _appLog.WriteLine(string.Format("WARNING encountered exception: {0}", _exceptionMsg));
                return false;
            } // End catch

            catch (Exception ex)
            {
                _exceptionMsg = ex.Message;
                _eventLog.WriteEvent("FAILED due to an Exception: " + _exceptionMsg);
                if (_appLog != null && _exceptionMsg != null) _appLog.WriteLine(string.Format("WARNING encountered exception: {0}", _exceptionMsg));
                return false;
            } // End catch

            finally
            {
                if (_loadedSettings.LogUpdates)
                {
                    if (_appLog != null && _exceptionMsg != null)
                    {
                        _appLog.WriteLine(string.Format("FAILED due to an exception: {0}", _exceptionMsg));
                     
                        if (_timesheet != null && _statusing != null)
                            _appLog.WriteLine(string.Format("SubmitStatus: {0}; timesheet URL: {1}; statusing URL: {2}; SiteGUID: {3}, timesheet GUID: {4}",
                                                            submitStatus.ToString(), _timesheet.Url, _statusing.Url, siteGuid, tsGuid));
                        _appLog.WriteLine(_tsInfo);
                    } // End if

                    else
                    {
                        if (_appLog != null)
                        {
                            _appLog.WriteLine(string.Format("SUCCESSFULY executed custom timesheet event {0}.",
                                                            _loadedSettings.Event));
                            _appLog.WriteLine(
                                string.Format("Total execution time: {0} seconds; SubmitStatus: {1} seconds\r\n",
                                              _timeToProcessAll.Elapsed.TotalSeconds.ToString("N"),
                                              _timeToProcessStatus.Elapsed.TotalSeconds.ToString("N")));
                        }
                    } // End else
                } // End if

                if (_spSite != null) _spSite.Dispose();                
                if (_eventLog != null) _eventLog.Dispose();
                FlushLog();
                _webRefMgr.Clear();
            } // End Finally
            #endregion // STEP 6
            
            return _result; 
        } // ProcessTimesheet()

        public void FlushLog()
        {
            if (_appLog != null)
            {
                _appLog.Close();
                _appLog = null;
            }
        } // FlushLog()
    #endregion // Methods
    }
}
